package com.chen.base.utils;

import cn.hutool.core.map.MapUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.MethodIntrospector;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.stereotype.Service;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * @Author: 陈锦龙
 * @Date: 2023/4/10 14:16
 */
@Service
@Slf4j
public class SpringContextHolder implements ApplicationContextAware, DisposableBean {

    private static ApplicationContext applicationContext = null;

    /** 实现ApplicationContextAware接口，注入ApplicationContext到静态变量中 */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        log.info("注入SpringContextHolder中的ApplicationContext静态变量");
        SpringContextHolder.applicationContext = applicationContext;
    }

    /**
     * 获取指定方法注解的bean
     * @param annotationType 注解类型
     * @param excludes 需要排除的class集
     * @return K: bean   V: 方法和注解
     */
    public static <A extends Annotation> Map<Object, Map<Method, A>> getBeansByMethodAnnotate(Class<A> annotationType,
                                                                                              Class<?>... excludes){
        String[] beanNames = applicationContext.getBeanNamesForType(Object.class, false, true);
        // 需要排除的beanName集
        Set<String> excludeBeanNameSet = new HashSet<>();
        if (excludes != null){
            for (Class<?> exclude : excludes) {
                String name = exclude.getSimpleName();
                excludeBeanNameSet.add(name.substring(0,1).toLowerCase() + name.substring(1));
                excludeBeanNameSet.add(exclude.getName());
            }
        }

        Map<Object, Map<Method, A>> beanMap = new HashMap<>();
        for (String beanName : beanNames) {
            // 排除对应的bean，解决bean初始化循环依赖的问题
            if (excludeBeanNameSet.contains(beanName)){
                continue;
            }
            if (applicationContext.findAnnotationOnBean(beanName, Lazy.class) != null){
                continue;
            }
            // 获取bean
            Object bean = getBean(beanName);
            // 过滤方法, 参考: org.springframework.context.event.EventListenerMethodProcessor.processBean
            try {
                // 获取类中所有的方法和注解对应关系Map
                Map<Method, A> annotatedMethods = MethodIntrospector.selectMethods(
                        bean.getClass(), (MethodIntrospector.MetadataLookup<A>) method ->
                        AnnotatedElementUtils.findMergedAnnotation(method, annotationType)
                );
                if (MapUtil.isNotEmpty(annotatedMethods)){
                    beanMap.put(bean, annotatedMethods);
                }
            } catch (Throwable ex) {
                log.error("method resolve error for bean[" + beanName + "].", ex);
            }
        }
        return beanMap;
    }

    /**
     * 动态注入单例bean实例
     * @param beanName bean名称
     * @param bean 单例bean实例
     * @return 注入实例
     */
    public static <T> void registerBean(String beanName, T bean){
        //将applicationContext转换为ConfigurableApplicationContext
        ConfigurableApplicationContext context = (ConfigurableApplicationContext) applicationContext;
        //获取BeanFactory
        DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) context.getAutowireCapableBeanFactory();
        //动态注册bean.
        beanFactory.registerSingleton(beanName, bean);
    }

    /** 从静态变量applicationContext中获取Bean，自动转型为所赋值对象的类型 */
    public static <T> T getBean(Class<T> requiredType){
        return applicationContext.getBean(requiredType);
    }

    /** 从静态变量applicationContext中获取Bean，自动转型为所赋值对象的类型 */
    public static <T> T getBean(String beanName){
        return (T)applicationContext.getBean(beanName);
    }

    /** 发布事件 */
    public static void publishEvent(ApplicationEvent event){
        applicationContext.publishEvent(event);
    }

    /** 关闭上下文 */
    public static void closeContext(){
        ((ConfigurableApplicationContext) applicationContext).close();
    }

    /** 获取存储在静态变量中的ApplicationContext */
    public static ApplicationContext getAppContext() {
        return applicationContext;
    }

    /** 获取当前环境字符串 */
    public static String getActiveProfile(){
        return applicationContext.getEnvironment().getActiveProfiles()[0];
    }

    /** 重要：实现DisposableBean接口，在Context关闭时清理静态变量 */
    @Override
    public void destroy() {
        log.info("清除SpringContextHolder中的ApplicationContext");
        SpringContextHolder.applicationContext = null;
    }
}
